﻿using System;
using System.Data;
using System.Windows.Forms;
using HighDiveAdmin.Engine;
using HighDiveEngine.Aggregates;
using HighDiveEngine.Model;
using HighDiveEngine.Resources;

namespace HighDiveAdmin.GUI
{
    public class FormMainPresenter
    {
        private readonly IHighDiveAdminEngine _appEngine;
        private readonly IFormMain _form;

        public FormMainPresenter(IHighDiveAdminEngine appEngine, IFormMain form)
        {
            _appEngine = appEngine;
            _appEngine.EventUpdateJudgeScore += _updateJudgeScore;
            _appEngine.EventUpdateDiveQueue += _updateDiveQueueHistoryActive;
            _appEngine.EventJudgeConnected += _updateJudgeStatus;
            _appEngine.EventContestCompleted += _contestCompleted;
            _form = form;
            _form.EventLoadWelcomeScreen += _loadWelcomeScreen;
            _form.EventLoadEditScreen += _loadEditScreen;
            _form.EventLoadEditScreenNewContest += _loadEditScreenNewContest;
            _form.EventLoadRunningScreen += _loadRunningScreen;
            _form.EventSelectContestToRun += _loadUnstartedContests;
            _form.EventLoadContestToEdit += _loadContestToEdit;
            _form.EventManualScoreEntry += _manualScoreEntry;
            _form.EventStartContest += _startActiveContest;
            _form.EventLoadDiver += _editContestScreenLoadDiver;
            _form.EventSaveContest += _saveContest;
           // _form.EventUpdateContestInformation += UpdateContestInformation;
            _form.EventNewJudge += _newJudge;
            _form.EventSetNumberOfJudges += _setNumberOfJudges;
            _form.EventEditContestLoadJudge += _editContestLoadJudge;
            _form.EventEndContest += _endContest;
            _form.EventUpdateActiveJudge += _loadJudgeToTextBox;
            _form.EventSaveJudge += _newJudge;
            //_form.EventCalculateDiveDifficulty += _editContest_CalculateDiveDifficulty;
            _form.EventCalculateDiveDifficulty += _editContestCalculateDiveDifficulty;
            _form.EventEditContestAddDiver += _editContestAddDiver;
            _form.EventEditContestLoadDiveList += _editContestLoadDiveList;
            _form.EventEditContestSaveDiver += _editContestSaveDiverToContest;
            _form.EventEditContestEditDiver += _editContestEditDiver;
            _form.EventDiscardContest += _loadEditScreenNewContest;
            _form.EventSaveEditedDive += _saveEditedDive;
            _form.EventEditRunningDive += _loadActiveViewingDive;
            _form.EventEditContestAddEditJudge += _editContest_EventAddEditJudge;
            _form.EventSaveAndStartContest += _editContest_EventSaveAndStartContest;
            _form.EventRemoveDive += _editContestRemoveDive;
            _form.EventRemoveDiver += _editContestRemoveDiver;
            _appEngine.LoadContest(1);
            _appEngine.ResetContest();
            _loadWelcomeScreen();

            _appEngine.LoadPersonList();
            _form.FillEditContestScreenContestList(_appEngine.LoadContestList(false, false));
            //_form.FillEditContestScreenWithContest(_appEngine.NewContest(), _appEngine.ActiveViewingPersonList);
        }

        private void _editContestRemoveDiver(DiverEventArgs diverEventArgs)
        {
            var diver = new Diver()
            {
                Address = diverEventArgs.Address,
                Club = diverEventArgs.Club,
                Email = diverEventArgs.Email,
                FirstName = diverEventArgs.FirstName,
                Gender = diverEventArgs.Gender,
                IdNumber = diverEventArgs.IdNumber,
                LastName = diverEventArgs.LastName,
                StartNumber = diverEventArgs.StartNumber,
                DbId = diverEventArgs.DbId
            };
            _appEngine.RemoveDiver(diver);
            _form.ShowEditContestDiverTreeView(_appEngine.Contest.DiveQueue);
        }

        private void _editContestRemoveDive(DiveEventArgs diveEventArgs)
        {
            var dive = new Dive()
            {
                DbId = diveEventArgs.DbId,
                DiveCode = diveEventArgs.DiveCode,
                Diver = diveEventArgs.Diver,
                DiveNumber = diveEventArgs.DiveNumber
            };
            _appEngine.RemoveDive(dive);
            _form.ShowEditContestDiverTreeView(_appEngine.Contest.DiveQueue);
        }


        private void _loadActiveViewingDive(int? id)
        {
            _appEngine.LoadActiveViewingDive(id);
        }

        private void _saveEditedDive(DiveEventArgs diveArgs)
        {
            var dive = new Dive {DiveCode = diveArgs.DiveCode, DiveHeight = diveArgs.DiveHeight, DiveScoreList = new DiveScoreList()};
            if (diveArgs.DiveScores != null)
            {
                foreach (var diveScoreArg in diveArgs.DiveScores)
                {
                    dive.DiveScoreList.Add(new DiveScore
                    {
                        Judge = _appEngine.GetJudge(diveScoreArg.JudgeNumber),
                        Score = diveScoreArg.Score
                    });
                } 
            }
            _appEngine.SaveActiveViewingDive(dive);
            _updateDiveQueueHistoryActive(_appEngine.Contest);
        }

        private void _loadJudgeToTextBox(string id, int number)
        {
            Judge judge;
            if (id == "")
            {
                judge = new Judge()
                {
                    Address = "",
                    Club = "",
                    Email = "",
                    FirstName = "",
                    LastName = "",
                    Gender = Gender.Na,
                    IdNumber = ""
                };
            }
            else
            {
                judge = _appEngine.LoadJudge(id);
            }
            _appEngine.SetActiveViewingJudge(judge);

            _form.ShowEditContestNewJudgePanel(judge, number);
        }

        private void _setNumberOfJudges(int number)
        {
            _appEngine.SetNumJudges(number);
            _form.ShowEditContestJudgesPanel(_appEngine.Contest.JudgeList);
        }

        private void _newJudge(string idnumber, string firstname, string lastname, string email, string club,
            string address, int judgenumber)
        {
            var judge = new Judge()
            {
                IdNumber = idnumber,
                FirstName = firstname,
                LastName = lastname,
                Club = club,
                Address = address,
                JudgeNumber = judgenumber
            };
            //_appEngine.updateJudgeList(judge);
            _appEngine.NewPerson(judge);
            _appEngine.updateJudgeList(judge);
        }

        private void UpdateContestInformation(ContestArgs contestArgs)
        {
            _appEngine.UpdateContestInformation(contestArgs);
        }

        private void _saveContest(ContestArgs contestArgs)
        {
            _appEngine.UpdateContestInformation(contestArgs);
            _appEngine.Contest.DiveQueue.GenerateStartOrder();
            _appEngine.SaveCompleteContest();
            _form.UpdateContestList(_appEngine.LoadContestList(false,false));
        }


        private void _startActiveContest(bool offlineMode)
        {
            _appEngine.StartContest(offlineMode);
            _appEngine.ConnectionPhase = false;
            _appEngine.SendActiveDiveToJudges();
            _form.SetRunningContestNumVisibleJudges(_appEngine.Contest.JudgeList);
            _form.HideRunningContestConnectionPhase();
        }



        private void _loadContestToEdit(Contest contest)
        {
            _appEngine.LoadPersonList();
            _form.FillEditContestScreenWithContest(_appEngine.LoadContest(contest.DbId), _appEngine.ActiveViewingPersonList);
        }        

        private void _loadUnstartedContests()
        {
            var contestList = _appEngine.LoadContestList(false, true);
            if (contestList.Count == 0)
                _form.ShowWelcomeScreenNoContestsLoadedError();
            else
                _form.ShowWelcomeScreenStartContestList(contestList);
        }

        private void _newJudge(JudgeEventArgs judgeEventArgs)
        {
            _appEngine.ActiveViewingJudge.IdNumber = judgeEventArgs.IdNumber;
            _appEngine.ActiveViewingJudge.FirstName = judgeEventArgs.FirstName;
            _appEngine.ActiveViewingJudge.LastName = judgeEventArgs.LastName;
            _appEngine.ActiveViewingJudge.Email = judgeEventArgs.Email;
            _appEngine.ActiveViewingJudge.Club = judgeEventArgs.Club;
            _appEngine.ActiveViewingJudge.Address = judgeEventArgs.Address;
            _appEngine.ActiveViewingJudge.Gender = judgeEventArgs.Gender;

            if (!_appEngine.ActiveViewingPersonList.Exists(x => x.IdNumber == _appEngine.ActiveViewingJudge.IdNumber))
            {
                _appEngine.ActiveViewingPersonList.Add(_appEngine.ActiveViewingJudge);
                _form.UpdatePersonLists(_appEngine.ActiveViewingPersonList);
            }

            _form.ShowEditContestJudgesPanel(_appEngine.Contest.JudgeList);
        }

        #region RunningContestScreen
        private void _contestCompleted(Contest contest)
        {
            _form.Invoke(new DelegateVoidDiveList(_form.FillRunningContestDiveHistory), contest.DiveHistory);
            _form.Invoke(new DelegateVoidDiveList(_form.FillRunningContestDiveResults), contest.DiveHistory.GetResultList());
            _form.Invoke(new DelegateVoidDiveList(_form.FillRunningContestDiveQueue), contest.DiveQueue);
            _form.Invoke(new MethodInvoker(_form.ShowRunningContestCompletedPanel));
        }

        private void _endContest()
        {
            _form.Invoke(new MethodInvoker(_form.ShowRunningContestSaveContestProgress));
            _appEngine.EndContest();
            _form.Invoke(new MethodInvoker(_form.CloseRunningContestSaveContestProgress));
            _form.ActivePanel = FormPanels.Welcome;
        }

        private void _updateJudgeStatus(Judge judge)
        {
            _form.Invoke(new DelegateVoidJudge(_form.UpdateRunningContestJudgeConnectionStatus), judge);
        }

        private void _manualScoreEntry(double score, int judgeNumber)
        {
            var judge = _appEngine.GetJudge(judgeNumber);
            var dive = _appEngine.GetActveDive();
            //_appEngine.SendManualScoreToJudge(judge, dive);
            _appEngine.UpdateDiveScore(new DiveScore
            {
                Judge = judge,
                Score = score
            }, dive);
        }

        private void _updateJudgeScore(DiveScore score, Dive dive)
        {
            if (_appEngine.Contest.DiveQueue.Count > 0)
            {
                if (dive.Equals(_appEngine.Contest.DiveQueue[0]))
                    _form.Invoke(new DelegateVoidDiveScore(_form.FillRunningContestJudgeScore), score);
                else
                    _form.Invoke(new DelegateVoidDive(_form.UpdateJudgeScoreInHistory), dive);
            }
        }

        private void _updateDiveQueueHistoryActive(Contest contest)
        {
            _form.Invoke(new DelegateVoidDiveList(_form.FillRunningContestDiveHistory), contest.DiveHistory);
            _form.Invoke(new DelegateVoidDiveList(_form.FillRunningContestDiveResults), contest.DiveHistory.GetResultList());
            _form.Invoke(new DelegateVoidDiveList(_form.FillRunningContestDiveQueue), contest.DiveQueue);
            _form.Invoke(new DelegateVoidDive(_form.FillRunningContestActiveDive), contest.DiveQueue[0]);
            foreach (var score in contest.DiveQueue[0].DiveScoreList)
                _form.Invoke(new DelegateVoidDiveScore(_form.FillRunningContestJudgeScore), score);
        }

        #endregion

        #region LoadScreens
        private void _loadRunningScreen(int? contestId)
        {
            _form.ActivePanel = FormPanels.Running;
            var contest = _appEngine.LoadContest(contestId);
            _updateDiveQueueHistoryActive(contest);
            _form.ShowRunningContestConnectionPhase(contest.JudgeList, _appEngine.GetAdminIp());
        }

        /// <summary>
        /// Opens the edit screen with an empty contest active
        /// </summary>
        private void _loadEditScreenNewContest()
        {
            _form.ActivePanel = FormPanels.Editview;
            _appEngine.LoadPersonList();
            _form.FillEditContestScreenContestList(_appEngine.LoadContestList(false, false));
            _form.FillEditContestScreenWithContest(_appEngine.NewContest(), _appEngine.ActiveViewingPersonList);
        }

        /// <summary>
        /// Opens the welcome screen
        /// </summary>
        private void _loadWelcomeScreen()
        {
            _form.ActivePanel = FormPanels.Welcome;
            _form.FillWelcomeScreenUpcomingContests(_appEngine.LoadContestList(true, true));
        }

        /// <summary>
        /// Opens the edit screen, only showing a list of contests from which to edit
        /// </summary>
        private void _loadEditScreen()
        {
            _form.ActivePanel = FormPanels.Editview;
            _appEngine.LoadPersonList();
            _form.FillEditContestScreenContestList(_appEngine.LoadContestList(false, false));
            //_form.FillEditContestScreenWithContest(_appEngine.NewContest(), _appEngine.ActiveViewingPersonList);
        } 
        #endregion

        #region EditContestScreen

        /// <summary>
        /// Sets the activeEditDiver and returns a reference to it
        /// </summary>
        private void _editContestAddDiver(int startNumber)
        {
            _appEngine.SetActiveViewingDiver(new Diver { StartNumber = startNumber });
            _appEngine.SetActiveViewingDiveList(new DiveList());
            _form.FillEditContestNewDiverPanel(_appEngine.ActiveViewingDiver, _appEngine.ActiveViewingDiveList);
        }

        /// <summary>
        /// Sets the activeEditDiveList and returns a reference to it
        /// </summary>
        private DiveList _editContestLoadDiveList(Diver diver)
        {
            if (diver.IdNumber == null)
                _appEngine.SetActiveViewingDiveList(new DiveList());
            else
            {
                //build a List<Dive> from existing dives belonging to this diver
                var listDives = _appEngine.Contest.DiveQueue.FindAll(x => x.Diver == diver);

                //build a DiveList and set ActiveViewingDiveList
                var diveList = new DiveList();
                diveList.AddRange(listDives);
                _appEngine.SetActiveViewingDiveList(diveList);
            }

            return _appEngine.ActiveViewingDiveList;
        }

        private void _editContestLoadJudge(string idnumber)
        {
            if (_appEngine.Contest.DiveQueue.Exists(x => x.Diver.IdNumber == idnumber))
                throw new Exception("This person is already assigned as a diver");
            if (_appEngine.Contest.JudgeList.Exists(x => x.IdNumber == idnumber) && _appEngine.ActiveViewingJudge.IdNumber != idnumber)
                throw new Exception("This person is already assigned as a judge");

            var person = _appEngine.ActiveViewingPersonList.Find(x => x.IdNumber == idnumber);

            var dbId = _appEngine.ActiveViewingJudge.DbId;
            var judgeNumber = _appEngine.ActiveViewingJudge.JudgeNumber;
            var idString = _appEngine.ActiveViewingJudge.IdString;
            if (person != null)
            {
                int index = _appEngine.Contest.JudgeList.FindIndex(x => x.Equals(_appEngine.ActiveViewingJudge));
                if (index != -1)
                {
                    _appEngine.Contest.JudgeList[index] = new Judge(person) { DbId = dbId, JudgeNumber = judgeNumber, IdString = idString };
                    _appEngine.ActiveViewingJudge = _appEngine.Contest.JudgeList[index];
                }

                _form.FillEditContestNewJudgePanel(_appEngine.ActiveViewingJudge);
            }
        }

        private void _editContestScreenLoadDiver(string idnumber)
        {
            if (_appEngine.Contest.JudgeList.Exists(x => x.IdNumber == idnumber))
                throw new Exception("This person is already assigned as a judge");

            if (_appEngine.Contest.DiveQueue.Exists(x => x.Diver.IdNumber == idnumber))
                _editContestEditDiver(idnumber);
            else
            {
                var person = _appEngine.ActiveViewingPersonList.Find(x => x.IdNumber == idnumber);

                var startNumber = _appEngine.ActiveViewingDiver.StartNumber;
                var dbId = _appEngine.ActiveViewingDiver.DbId;
                if (person != null)
                {
                    _appEngine.SetActiveViewingDiver(new Diver(person) { DbId = dbId, StartNumber = startNumber });
                    _form.FillEditContestExistingDiverFromIdNumber(person);
                }
            }
        }


        private void _editContest_EventSaveAndStartContest(ContestArgs contestArgs)
        {
            _saveContest(contestArgs);
            _form.ActivePanel = FormPanels.Running;
            _updateDiveQueueHistoryActive(_appEngine.Contest);
            _form.ShowRunningContestConnectionPhase(_appEngine.Contest.JudgeList, _appEngine.GetAdminIp());
        }

        private Judge _editContest_EventAddEditJudge(int judgeNumber)
        {
            var judge = _appEngine.Contest.JudgeList.Find(x => x.JudgeNumber == judgeNumber);
            if (judge == null)
            {
                _appEngine.ActiveViewingJudge = new Judge { JudgeNumber = judgeNumber };
                _appEngine.Contest.JudgeList.Add(_appEngine.ActiveViewingJudge);
            }
            else
            {
                _appEngine.ActiveViewingJudge = judge;
            }
            return _appEngine.ActiveViewingJudge;
        }


        private void _editContestEditDiver(string idNumber)
        {
            _appEngine.ActiveViewingDiveList = new DiveList();
            _appEngine.ActiveViewingDiveList.AddRange(_appEngine.Contest.DiveQueue.FindAll(x => x.Diver.IdNumber == idNumber));
            _appEngine.ActiveViewingDiver = _appEngine.ActiveViewingDiveList[0].Diver;
            _form.FillEditContestNewDiverPanel(_appEngine.ActiveViewingDiver, _appEngine.ActiveViewingDiveList);
        }

        private void _editContestSaveDiverToContest(DiverEventArgs diverArgs, DiveEventArgs[] diveArgs)
        {
            _appEngine.ActiveViewingDiver.IdNumber = diverArgs.IdNumber;
            _appEngine.ActiveViewingDiver.FirstName = diverArgs.FirstName;
            _appEngine.ActiveViewingDiver.LastName = diverArgs.LastName;
            _appEngine.ActiveViewingDiver.Gender = diverArgs.Gender;
            _appEngine.ActiveViewingDiver.Address = diverArgs.Address;
            _appEngine.ActiveViewingDiver.Email = diverArgs.Email;
            _appEngine.ActiveViewingDiver.Club = diverArgs.Club;

            //build/update dives
            foreach (DiveEventArgs args in diveArgs)
            {
                var dive = _appEngine.ActiveViewingDiveList.Find(x => x.DiveNumber == args.DiveNumber);
                if (dive == null)
                {
                    _appEngine.Contest.DiveQueue.Add(new Dive
                    {
                        DiveNumber = args.DiveNumber,
                        DiveCode = args.DiveCode,
                        DiveHeight = args.DiveHeight,
                        Difficulty = args.Difficulty,
                        Diver = _appEngine.ActiveViewingDiver
                    });
                }
                else
                {
                    dive.DiveCode = args.DiveCode;
                    dive.DiveHeight = args.DiveHeight;
                    dive.Difficulty = args.Difficulty;
                }

            }

            if (!_appEngine.ActiveViewingPersonList.Exists(x => x.IdNumber == _appEngine.ActiveViewingDiver.IdNumber))
            {
                _appEngine.ActiveViewingPersonList.Add(_appEngine.ActiveViewingDiver);
                _form.UpdatePersonLists(_appEngine.ActiveViewingPersonList);
            }

            _appEngine.ActiveViewingDiver = null;
            _appEngine.ActiveViewingDiveList = null;
            _form.ShowEditContestDiverTreeView(_appEngine.Contest.DiveQueue);
        }

        private double _editContestCalculateDiveDifficulty(string diveCode, int height)
        {
            return DiveCodeDifficultyCalculator.Instance.GetDifficulty(diveCode, height);
        }
        #endregion
    }
}
